# UnitAttachment module.
# Handles some of the affects of a unit attachment (such as plague/def matrix/etc)
#

import CvUtil
from CvPythonExtensions import *

# globals
gc = CyGlobalContext()

# CONSTANTS
# Because sometimes you're just too lazy to create new XML data.
STIM_MOVEMENT_INCREASE = 2

BLIND_VISIBILITY_MODIFIER_PERCENT = 100

LOCKDOWN_MOVEMENT_PERCENT = 100

DEFENSIVE_MATRIX_HIT_POINTS = 250

IRRADIATE_CAST_DAMAGE = 8
IRRADIATE_SPLASH_RANGE = 1 # Affects a 3x3 matrix of plots around the unit
IRRADIATE_TURN_DAMAGE = 8

STASIS_FIELD_MOVEMENT_PERCENT = 100

MAELSTROM_MOVEMENT_PERCENT = 100

PLAGUE_CAST_DAMAGE = 30
PLAGUE_TURN_DAMAGE = 30

ENSNARE_MOVEMENT_PERCENT = 50



#
# The actual attachment events. The args used:

# onCast:
# pUnit: The unit being afflicted by the attachment
# eAfflictingPlayer: The person who's attachment is to be blamed.
# bFirstAttachment: True if this is the first (and thus only) attachment of this type to be on the unit at this time.
# Note that "first" means there were none other at the time of it's casting, not the first ever.
# This is useful for being used to track who's doing damage.
#

#
# onTurn:
# pUnit: The unit being afflicted by the attachment
# eAfflictingPlayer: The person who's attachment is to be blamed.
# This is useful for being used to track who's doing damage.
# Note: This is only called once for each attachment, even if a unit has more than one of this type of attachment.
#

#
# onRemove:
# pUnit: The unit that the attachment is being removed from.
# eAfflictingPlayer: The person who's attachment is being removed.
# bFinalAttachment: True if this is the last attachment of this type to be removed from the unit (no more remain).
#
# Note that the fact that a player's attachment is removed last shouldn't be taken as
# anything in terms of the fact that that player's attachment ultimately did something.
# Unlike the onTurn and onCast functions, it just helps determine whos attachment it was,
# and shouldn't be used for dealing damage or other such things.
#



#
# Stim
# GERIKESTODO: Attack moves increase

def onStimCast(pUnit, eAfflictingPlayer, bFirstAttachment):
    if bFirstAttachment:
        pUnit.changeMaxMovesPersistentModifier(STIM_MOVEMENT_INCREASE)

def onStimTurn(pUnit, eAfflictingPlayer):
    pass

def onStimRemove(pUnit, eAfflictingPlayer, bFinalAttachment):
    if (bFinalAttachment):
        pUnit.changeMaxMovesPersistentModifier(-STIM_MOVEMENT_INCREASE)

#
# Lockdown
#

def onLockdownCast(pUnit, eAfflictingPlayer, bFirstAttachment):
    if bFirstAttachment:
        pUnit.changeMaxMovesPersistentModifierPercent(-LOCKDOWN_MOVEMENT_PERCENT)

def onLockdownTurn(pUnit, eAfflictingPlayer):
    pass

def onLockdownRemove(pUnit, eAfflictingPlayer, bFinalAttachment):
    if bFinalAttachment:
        pUnit.changeMaxMovesPersistentModifierPercent(LOCKDOWN_MOVEMENT_PERCENT)


#
# Defensive Matrix
#

def onDefensiveMatrixCast(pUnit, eAfflictingPlayer, bFirstAttachment):
    pUnit.setDefensiveMatrixShield(DEFENSIVE_MATRIX_HIT_POINTS)

def onDefensiveMatrixTurn(pUnit, eAfflictingPlayer):
    pass

def onDefensiveMatrixRemove(pUnit, eAfflictingPlayer, bFinalAttachment):
    if bFinalAttachment:
        pUnit.setDefensiveMatrixShield(0)


#
# Irradiate
#

def canBeIrradiated(pUnit):
    if pUnit.isBuilding():
        return False

    if pUnit.isInvulnerable():
        return False

    if not gc.getUnitInfo(pLoopUnit.getUnitType()).isBiological():
        return False

    return True



def onIrradiateCast(pUnit, eAfflictingPlayer, bFirstAttachment):
    if bFirstAttachment:
        if gc.getUnitInfo(pUnit.getUnitType()).isBiological():
            pUnit.doDamage(IRRADIATE_CAST_DAMAGE, eAfflictingPlayer)
        for iX in range(pUnit.getX() - IRRADIATE_SPLASH_RANGE, pUnit.getX() + IRRADIATE_SPLASH_RANGE + 1):
            for iY in range(pUnit.getY() - IRRADIATE_SPLASH_RANGE, pUnit.getY() + IRRADIATE_SPLASH_RANGE + 1):
                pPlot = CyMap().plot(iX, iY)
                for iUnit in range(CyMap().plot(iX, iY).getNumUnits()):
                    pLoopUnit = pPlot.getUnit(iUnit)
                    # Make sure it's not affecting the same unit.
                    if not (pLoopUnit.getID() == pUnit.getID() and pLoopUnit.getOwner() == pUnit.getOwner() ):
                        if gc.getUnitInfo(pLoopUnit.getUnitType()).isBiological():
                            pLoopUnit.doDamage(IRRADIATE_CAST_DAMAGE, eAfflictingPlayer)
        

def onIrradiateTurn(pUnit, eAfflictingPlayer):
    if gc.getUnitInfo(pUnit.getUnitType()).isBiological():
        pUnit.doDamage(IRRADIATE_TURN_DAMAGE, eAfflictingPlayer)
    for iX in range(pUnit.getX() - IRRADIATE_SPLASH_RANGE, pUnit.getX() + IRRADIATE_SPLASH_RANGE + 1):
        for iY in range(pUnit.getY() - IRRADIATE_SPLASH_RANGE, pUnit.getY() + IRRADIATE_SPLASH_RANGE + 1):
            pPlot = CyMap().plot(iX, iY)
            for iUnit in range(CyMap().plot(iX, iY).getNumUnits()):
                pLoopUnit = pPlot.getUnit(iUnit)
                # Make sure it's not affecting the same unit.
                if not (pLoopUnit.getID() == pUnit.getID() and pLoopUnit.getOwner() == pUnit.getOwner() ):
                    if gc.getUnitInfo(pLoopUnit.getUnitType()).isBiological():
                        pLoopUnit.doDamage(IRRADIATE_TURN_DAMAGE, eAfflictingPlayer)
    pass

def onIrradiateRemove(pUnit, eAfflictingPlayer, bFinalAttachment):
    pass


#
# Blind
#

def onBlindCast(pUnit, eAfflictingPlayer, bFirstAttachment):
    if bFirstAttachment:
        pUnit.changeVisibilityPersistentModifierPercent(-1 * BLIND_VISIBILITY_MODIFIER_PERCENT)

def onBlindTurn(pUnit, eAfflictingPlayer):
    pass

def onBlindRemove(pUnit, eAfflictingPlayer, bFinalAttachment):
    if bFinalAttachment:
        pUnit.changeVisibilityPersistentModifierPercent(BLIND_VISIBILITY_MODIFIER_PERCENT)
        


#
# Stasis Field
#

def onStasisFieldCast(pUnit, eAfflictingPlayer, bFirstAttachment):
    if bFirstAttachment:
        pUnit.changeMaxMovesPersistentModifierPercent(-STASIS_FIELD_MOVEMENT_PERCENT)
        pUnit.setInvulnerable(True)

def onStasisFieldTurn(pUnit, eAfflictingPlayer):
    pass

def onStasisFieldRemove(pUnit, eAfflictingPlayer, bFinalAttachment):
    if bFinalAttachment:
        pUnit.changeMaxMovesPersistentModifierPercent(STASIS_FIELD_MOVEMENT_PERCENT)
        pUnit.setInvulnerable(False)


#
# Maelstrom
#

def onMaelstromCast(pUnit, eAfflictingPlayer, bFirstAttachment):
    if bFirstAttachment:
        pUnit.changeMaxMovesPersistentModifierPercent(-MAELSTROM_MOVEMENT_PERCENT)

def onMaelstromTurn(pUnit, eAfflictingPlayer):
    pass

def onMaelstromRemove(pUnit, eAfflictingPlayer, bFinalAttachment):
    if bFinalAttachment:
        pUnit.changeMaxMovesPersistentModifierPercent(MAELSTROM_MOVEMENT_PERCENT)


#
# Plague
#

def onPlagueCast(pUnit, eAfflictingPlayer, bFirstAttachment):
    # Don't kill a unit with plague...just set their HP to 1
    if bFirstAttachment:
        if (pUnit.currHitPoints() <= PLAGUE_CAST_DAMAGE):
            pUnit.setHitPoints(1)
        else:
            pUnit.changeHitPoints(-PLAGUE_CAST_DAMAGE)

def onPlagueTurn(pUnit, eAfflictingPlayer):
    # Don't kill a unit with plague...just set their HP to 1
    if (pUnit.currHitPoints() <= PLAGUE_TURN_DAMAGE):
        pUnit.setHitPoints(1)
    else:
        pUnit.changeHitPoints(-PLAGUE_TURN_DAMAGE)

def onPlagueRemove(pUnit, eAfflictingPlayer, bFinalAttachment):
    pass

#
# Parasite
#

def onParasiteCast(pUnit, eAfflictingPlayer, bFirstAttachment):
    pUnit.changeStolenVisibilityCount(gc.getPlayer(eAfflictingPlayer).getTeam(), 1)

def onParasiteTurn(pUnit, eAfflictingPlayer):
    pass

def onParasiteRemove(pUnit, eAfflictingPlayer, bFinalAttachment):
    pUnit.changeStolenVisibilityCount(gc.getPlayer(eAfflictingPlayer).getTeam(), -1)
    pass


#
# Ensnare
#

def onEnsnareCast(pUnit, eAfflictingPlayer, bFirstAttachment):
    if bFirstAttachment:
        pUnit.changeMaxMovesPersistentModifierPercent(-ENSNARE_MOVEMENT_PERCENT)

def onEnsnareTurn(pUnit, eAfflictingPlayer):
    pass

def onEnsnareRemove(pUnit, eAfflictingPlayer, bFinalAttachment):
    if bFinalAttachment:
        pUnit.changeMaxMovesPersistentModifierPercent(ENSNARE_MOVEMENT_PERCENT)


# The actual handler.
# For every attachment that needs handling there is a tuple in the dictionary
# The first part of the tuple is the function that is called when the attachment is cast.
# The second part of the tuple is the function that is called when the attachment has it's turn run (called every turn).
# The third part of the tuple is the function that is called when the attachment is removed.
pAttachmentHandlerDict = {
    gc.getInfoTypeForString("UNIT_ATTACHMENT_STIM_PACK") : (onStimCast, onStimTurn, onStimRemove),
    gc.getInfoTypeForString("UNIT_ATTACHMENT_LOCKDOWN") : (onLockdownCast, onLockdownTurn, onLockdownRemove),
    gc.getInfoTypeForString("UNIT_ATTACHMENT_DEFENSIVE_MATRIX") : (onDefensiveMatrixCast, onDefensiveMatrixTurn, onDefensiveMatrixRemove),
    gc.getInfoTypeForString("UNIT_ATTACHMENT_IRRADIATE") : (onIrradiateCast, onIrradiateTurn, onIrradiateRemove),
    gc.getInfoTypeForString("UNIT_ATTACHMENT_BLIND") : (onBlindCast, onBlindTurn, onBlindRemove),
    gc.getInfoTypeForString("UNIT_ATTACHMENT_STASIS_FIELD") : (onStasisFieldCast, onStasisFieldTurn, onStasisFieldRemove),
    gc.getInfoTypeForString("UNIT_ATTACHMENT_MAELSTROM") : (onMaelstromCast, onMaelstromTurn, onMaelstromRemove),
    gc.getInfoTypeForString("UNIT_ATTACHMENT_PLAGUE") : (onPlagueCast, onPlagueTurn, onPlagueRemove),
    gc.getInfoTypeForString("UNIT_ATTACHMENT_PARASITE") : (onParasiteCast, onParasiteTurn, onParasiteRemove),
    gc.getInfoTypeForString("UNIT_ATTACHMENT_PLAGUE") : (onPlagueCast, onPlagueTurn, onPlagueRemove),
    gc.getInfoTypeForString("UNIT_ATTACHMENT_ENSNARE") : (onEnsnareCast, onEnsnareTurn, onEnsnareRemove)
}
                 
# Forwards the attachments onCast event to the correct function.
def handleAttachmentOnCast(argsList):
    pUnit, eAttachmentType, eAfflictingPlayer, bFirstAttachment = argsList
    print "Handling onCast for %d" % eAttachmentType
    if pAttachmentHandlerDict.has_key(eAttachmentType):
        apply(pAttachmentHandlerDict[eAttachmentType][0], (pUnit, eAfflictingPlayer, bFirstAttachment))

# Forwards the attachment's onTurn event to the correct function.
def handleAttachmentOnTurn(argsList):
    pUnit, eAttachmentType, eAfflictingPlayer = argsList
    if pAttachmentHandlerDict.has_key(eAttachmentType):
        apply(pAttachmentHandlerDict[eAttachmentType][1], (pUnit, eAfflictingPlayer))

# Forwards the attachment's onRemove event to the correct function.
def handleAttachmentOnRemove(argsList):
    pUnit, eAttachmentType, eAfflictingPlayer, bLastAttachment = argsList
    if pAttachmentHandlerDict.has_key(eAttachmentType):
        apply(pAttachmentHandlerDict[eAttachmentType][2], (pUnit,eAfflictingPlayer,bLastAttachment) )






